package org.netty.demo.lihh.notice.server;

import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;
import org.netty.demo.lihh.notice.domain.ClientMsgProtocol;
import org.netty.demo.lihh.notice.strategy.factory.StrategyFactory;
import org.netty.demo.lihh.notice.util.ChannelHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static io.netty.buffer.Unpooled.copiedBuffer;

public class MyServerHandler extends ChannelInboundHandlerAdapter {
  private static final Logger log = LoggerFactory.getLogger(MyServerHandler.class);
  private WebSocketServerHandshaker handShaker;
  
  /**
   * 当客户端主动链接服务端的链接后，这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
   *
   * @author lihh
   */
  @Override
  public void channelActive(ChannelHandlerContext ctx) throws Exception {
    SocketChannel channel = (SocketChannel) ctx.channel();
    
    log.info("链接报告开始 ~~~");
    log.info("链接报告信息：有一客户端链接到本服务端");
    log.info("链接报告IP:{}", channel.remoteAddress().getHostString());
    log.info("链接报告Port:{}", channel.remoteAddress().getPort());
    log.info("链接报告完毕 ~~~");
    ChannelHandler.addChannel(ctx.channel());
  }
  
  /**
   * 这里是 连接失效的时候
   *
   * @param ctx socket channel 上下文
   * @author lihh
   */
  @Override
  public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    SocketChannel channel = (SocketChannel) ctx.channel();
    
    String tk = ChannelHandler.getTkByChannel(ctx.channel());
    // 根据 tk 删除 channel 信息
    ChannelHandler.removeChannel(tk);
    
    log.info("链接退出报告开始 ~~~");
    log.info("链接退出报告信息：存在一个客户端关闭连接");
    log.info("链接退出报告IP:{}", channel.remoteAddress().getHostString());
    log.info("链接退出报告Port:{}", channel.remoteAddress().getPort());
    log.info("退出的tk是:{}", tk);
    log.info("链接退出报告完毕 ~~~");
  }
  
  /**
   * 这里是 处理读消息
   *
   * @param ctx socket 上下文
   * @param msg 消息对象
   * @author lihh
   */
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    // 处理 协议升级 相关的事情
    if (msg instanceof FullHttpRequest) {
      
      FullHttpRequest httpRequest = (FullHttpRequest) msg;
      
      if (!httpRequest.decoderResult().isSuccess()) {
        
        DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
        
        // 返回应答给客户端
        if (httpResponse.status().code() != 200) {
          ByteBuf buf = copiedBuffer(httpResponse.status().toString(), CharsetUtil.UTF_8);
          httpResponse.content().writeBytes(buf);
          buf.release();
        }
        
        // 如果是非Keep-Alive，关闭连接
        ChannelFuture f = ctx.channel().writeAndFlush(httpResponse);
        if (httpResponse.status().code() != 200) {
          f.addListener(ChannelFutureListener.CLOSE);
        }
        
        return;
      }
      
      WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws:/" + ctx.channel() + "/websocket", null, false);
      handShaker = wsFactory.newHandshaker(httpRequest);
      
      if (null == handShaker) {
        WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
      } else {
        handShaker.handshake(ctx.channel(), httpRequest);
      }
      return;
    }
    
    // 处理 ws
    if (msg instanceof WebSocketFrame) {
      WebSocketFrame webSocketFrame = (WebSocketFrame) msg;
      
      //关闭请求
      if (webSocketFrame instanceof CloseWebSocketFrame) {
        handShaker.close(ctx.channel(), (CloseWebSocketFrame) webSocketFrame.retain());
        return;
      }
      
      //ping请求
      if (webSocketFrame instanceof PingWebSocketFrame) {
        ctx.channel().write(new PongWebSocketFrame(webSocketFrame.content().retain()));
        return;
      }
      
      //只支持文本格式，不支持二进制消息
      if (!(webSocketFrame instanceof TextWebSocketFrame)) {
        throw new Exception("仅支持文本格式");
      }
      
      // 拿到发送的消息
      String request = ((TextWebSocketFrame) webSocketFrame).text();
      ClientMsgProtocol clientMsgProtocol = JSON.parseObject(request, ClientMsgProtocol.class);
      
      // 客户端请求状态
      Channel currChannel = ctx.channel();
      // 如果是连接状态的话，设置 channelId
      if (1 == clientMsgProtocol.getState().getType())
        clientMsgProtocol.setChannelId(ChannelHandler.getChannelId(currChannel));
      
      // 执行策略
      StrategyFactory.execute(clientMsgProtocol.getState().getType(), clientMsgProtocol);
    }
  }
}
